import { hasChanged, isObject } from "../utils";
import { createDep } from "./dep";
import { isProxy, isReactive, reactive, ReactiveFlags } from "./reactive";
import { triggerEffect, trackEffect } from "./effect";

export function ref(value: any) {
  if (isRef(value)) {
    return value;
  }
  return new RefImpl(value);
}

export class RefImpl {
  private _value;
  private _rawValue;

  __v_isRef = true;

  public deps;

  constructor(value: any) {
    this._value = reactive(value);

    this._rawValue = toRaw(value);

    this.deps = createDep();
  }

  get __v_raw() {
    return this._rawValue;
  }

  get value() {
    trackRef(this, "get");
    return this._value;
  }

  set value(newValue) {
    if (hasChanged(this._value, newValue)) {
      const oldValue = this._rawValue;
      this._value = reactive(newValue);
      this._rawValue = toRaw(newValue);
      triggerRef(this, "set", newValue, oldValue);
    }
  }
}

export function isRef(target) {
  return !!target.__v_isRef;
}

export function toRaw(target: any) {
  const raw = target && target[ReactiveFlags.RAW];
  return raw ? toRaw(raw) : target;
}

function trackRef(target, type) {
  trackEffect(target.deps);
}

export function triggerRef(target, type, newValue, oldValue) {
  triggerEffect(target.deps);
}
